/*
 * Copyright (c) Zhongkehonglue Technologies Co., Ltd. 2025. All rights reserved.
 * Description: Implementation of functions related to the DMC DEV module.
 * Author: wangchunyang
 * Create: 2025-05-23
 */

#include "dmcEnv.h"

#include <securec.h>
#include "dmcLog.h"
#include "dmcEvent.h"
#include "dmcConstant.h"
#include "dmcConstant.h"
#include "dmcCallback.h"
#include "dmcDevice.h"

#ifdef LOG_TAG
#undef LOG_TAG
#endif

namespace DlnaDmc {

DmcEnv DmcEnv::instance;

dmc_int32 DmcEnv::InitEnv(const std::string &ifName, const DmcCbType &cb)
{
    std::lock_guard<std::mutex> guard(initMtx);
    LOGI("Init env begin, ifName=%{public}s.", ifName.c_str());

    auto ret =DmcCallback::GetInstance().SetCb(cb);
    if (ret != DMC_SUCCESS) {
        return ret;
    }

    ret = DMC_FAILURE;
    do {
        ret = UpnpInit2(ifName.c_str(), 0);
        if (ret != UPNP_E_SUCCESS) {
            LOGE("Init upnp failed(%{public}ld)", ret);
            ret = DMC_FAILURE;
            break;
        }
        if (UpnpSetMaxContentLength(0) != UPNP_E_SUCCESS) {
            LOGE("Could not set max content UPnP");
            ret = DMC_FAILURE;
            break;
        }

        ret = UpnpRegisterClient(DmcEventHandlerCb, nullptr, &clientHdl);
        if (ret != UPNP_E_SUCCESS) {
            LOGE("Register control point failed, ret is %{public}u", ret);
            ret = DMC_FAILURE;
            break;
        }
        ret = SendMSearch();
        if (ret != DMC_SUCCESS) {
            break;
        }
        task.start();
        ret = DMC_SUCCESS;
    } while(0);
    if (ret == DMC_SUCCESS) {
        LOGI(" init env succeeded.");
        envInited = true;
    } else {
        LOGE(" init env failed.");
        envInited = false;
        DestroyEnv();
    }
    return ret;
}

bool DmcEnv::IsInited()
{
    return envInited;
}

dmc_int32 DmcEnv::DestroyEnv()
{
    std::lock_guard<std::mutex> guard(initMtx);
    dmc_int32 ret = DMC_SUCCESS;
    auto upnpRet = UpnpUnRegisterClient(clientHdl);
    if (upnpRet != UPNP_E_SUCCESS) {
        LOGE("UpnpUnRegisterClient failed.");
        ret = DMC_FAILURE;
    }
    upnpRet = UpnpFinish();
    if (upnpRet != UPNP_E_SUCCESS) {
        LOGE("UpnpFinish failed.");
        ret = DMC_FAILURE;
    }
    ret =DmcCallback::GetInstance().ReSetCb();
    if (ret != DMC_SUCCESS) {
        LOGE("cb reset failed.");
        ret = DMC_FAILURE;
    }

    ret =DmcDevice::GetInstance().ReSet();
    if (ret != DMC_SUCCESS) {
        LOGE("dmc device reset failed.");
        ret = DMC_FAILURE;
    }
    task.stop();
    envInited = false;
    return ret;
}

dmc_int32 DmcEnv::ReSearchAsync(void *data)
{
    LOGI("reresearch async.");
    if (data == nullptr) {
        LOGE("Sending Search request nullptr.");
        return DMC_FAILURE;
    }
    UpnpDevice_Handle dev = (UpnpDevice_Handle)data;
    auto ret = UpnpSearchAsync(dev, DMC_MSEARCH_TIMEOUT, "urn:schemas-upnp-org:device:MediaRenderer:1", NULL);
    if (ret != DMC_SUCCESS) {
        LOGE("Sending Search request For DMR failed with return code:"
            "%{public}ld", ret);
        return ret;
    }
    return ret;
}

int UpnpRandom(void)
{
    int fd = open("/dev/urandom", O_RDONLY);
    if (fd > 0) {
        int r = 0;
        (void)read(fd, &r, sizeof(int));
        close(fd);
        return abs(r);
    } else {
        close(fd);
        return 0;
    }
}

dmc_int32 DmcEnv::SendMSearch()
{
    auto retVal = UpnpSearchAsync(clientHdl, DMC_MSEARCH_TIMEOUT,
                             "urn:schemas-upnp-org:device:MediaRenderer:1", nullptr);
    if (retVal != DMC_SUCCESS) {
        LOGE("DMR search request failed, ret code: %{public}ld", retVal);
        return DMC_FAILURE;
    }

    ThreadPoolJob job;
    // Resend the DMC Msearch  , Schedule the Search job for the Random Time.
    if (memset_s(&job, sizeof(ThreadPoolJob), 0, sizeof(ThreadPoolJob)) != EOK) {
        LOGE("memset_s fail!");
    }
    (void)TPJobInit(&job, (start_routine)ReSearchAsync, (void *)(intptr_t)clientHdl);
    (void)TPJobSetFreeFunction(&job, nullptr);

    (void)TimerThreadSchedule(&gTimerThread, (UpnpRandom() % DMC_COMMON_DIGIT_MODULE), REL_SEC, &job, SHORT_TERM,
        NULL);

    return retVal;
}

dmc_int32 DmcEnv::GetClientHdl()
{
    return clientHdl;
}
} // DlnaDmc






